import React, { useReducer, createContext } from 'react';
import { message } from 'antd';
import get from 'lodash/get';
import findIndex from 'lodash/findIndex';
import cloneDeep from 'lodash/cloneDeep';
import set from 'lodash/set';
import last from 'lodash/last';
import isEmpty from 'lodash/isEmpty';
import { mapNodes, hasChildren, getUniqueID, filterNodes, isEmptyObj } from '@/utils/utils';
import { setValue, getValue, getCurrentGroups, getCurrentGroup, hasGroups } from './utils';
import { ARRAY_TYPE, OBJECT_TYPE, STRING_TYPE } from './settings';
import { ARRAY_MAPPING, LEAF_MAPPING, OBJECT_MAPPING, CONDITION_TRUE } from '../../settings';
import { getDefaultNode } from '@/pages/Validator/utils';
import isEqual from 'lodash/isEqual';

function getRangeCode(len = 6) {
  const orgStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  let returnStr = '';
  for (let i = 0; i < len; i++) {
    returnStr += orgStr.charAt(Math.floor(Math.random() * orgStr.length));
  }
  return returnStr;
}

// 父节点如果为配置，需要自动添加
// undefined 表示根节点
// prefix: 由2个部分（动态的）组成[...parent.prefix, parent.groupIndex]
// indexes: 由2个部分组成[...prefix, 父级副本下当前副本添加的顺序]
function fillParentDefaultRule(parent, current, newData) {
  if (parent !== undefined) {
    const parentIds = current.parents;
    // eslint-disable-next-line prefer-destructuring
    newData = mapNodes([newData], k => {
      if (parentIds.includes(k.id)) {
        const parentGroup = getCurrentGroup(k);
        const defaultRule = {
          id: getUniqueID('rule'),
          condition: {
            type: CONDITION_TRUE,
          },
          config: {
            mapping: [],
            type: k.nodeType === ARRAY_TYPE ? ARRAY_MAPPING : OBJECT_MAPPING,
          },
        };
        // 父节点未配置
        if (parentGroup === undefined) {
          const { prefix, groupIndex } = k;
          const d = {
            groupName: `副本_${getRangeCode()}`,
            indexes: [...prefix, groupIndex],
            rules: [defaultRule],
          };

          const arr = hasGroups(k) ? k.groups : [];
          k.groups = [...arr, d];
        }

        // 父节点规则为空
        else if (isEmpty(parentGroup.rules)) {
          parentGroup.rules = [defaultRule];
        }
      }

      return k;
    })[0];
  }
  return newData;
}

// 1. 创建全局的Context
const Context = createContext();

export default Context;

// 2. 创建全局的Reducer
const initState = {
  targetNodePath: [],
  sourceNodePath: [],
  data: null,
  globalToggle: { timestamp: +new Date() - 1000 * 2, status: true }, // 是否展开全部子节点
};

function reducer(state, action) {
  const { type, payload } = action;

  if (type === 'set_the_whole_tree') {
    return {
      ...state,
      ...payload,
    };
  }

  if (type === 'open_or_close_node') {
    return {
      ...state,
      data: mapNodes([state.data], k => {
        const a = set({}, 'expanded', payload);
        const s = k.state || {};
        k.state = { ...s, ...a };
        return k;
      })[0],
      globalToggle: { timestamp: +new Date(), status: payload },
    };
  }

  // 修改节点名称
  if (type === 'modify_node_name') {
    const { node, nodePath, newName } = payload;

    const _newData = mapNodes([node], k => {
      const parentPathLength = node.nodePath.length;

      const newNodePath = k.nodePath.map((each, index) =>
        index === parentPathLength - 1 ? newName : each
      );

      if (k.id === node.id) {
        return {
          ...k,
          nodeName: newName,
          nodePath: newNodePath,
        };
      }

      return {
        ...k,
        nodePath: newNodePath,
      };
    });

    const newData = setValue(state.data, nodePath, _newData[0]);

    return {
      ...state,
      data: newData,
    };
  }

  // 更新节点nodeType,适用于除`rules`外的普通属性。
  if (type === 'update_node_normal_attr') {
    const { value, path: nodeAttrPath, attrName, parent = {}, current } = payload;

    let newData = setValue(state.data, nodeAttrPath, value);

    const a = nodeAttrPath.split('.');
    a.pop();
    const nodePath = a.join('.');

    // 当nodeType切换时，需要额外处理children属性。只允许`object` 和 `array`有子节点
    if (attrName === 'nodeType') {
      const node = getValue(newData, nodePath);

      if (![OBJECT_TYPE, ARRAY_TYPE].includes(value)) {
        delete node.children;
        node.groups = [];

        newData = setValue(state.data, nodePath, node);
      } else {
        // 主动追加子节点，避免再手动点
        const p = get(parent, 'prefix') || [];
        const g = get(parent, 'groupIndex') || 0;

        const isRoot = isEmptyObj(parent);

        const defaultAttrs = {
          groupIndex: 0,
          prefix: isRoot ? [] : [...p, g],
          groups: [],
        };

        const c = [
          {
            ...getDefaultNode(
              { ...current, nodeType: value },
              value === ARRAY_TYPE ? OBJECT_TYPE : STRING_TYPE
            ),
            nodeName: value === ARRAY_TYPE ? 'items' : getUniqueID('field'),
            state: { expanded: true },
            groupIndex: 0,
            prefix: [...(get(node, 'prefix') || []), get(node, 'groupIndex') || 0],
            groups: [],
          },
        ];

        newData = setValue(state.data, nodePath, {
          ...node,
          state: { expanded: true },
          children: c,
          ...defaultAttrs,
        });
      }
    }

    return {
      ...state,
      data: newData,
    };
  }

  if (type === 'quick_mapping') {
    const { sourceNodePath, targetNodePath, current, parent } = payload;

    const ins = getCurrentGroup(current);

    // const current = targetNodePath === '' ? state.data : get(state.data, targetNodePath);

    // const parent = getNodeById([state.data], last(current.parents))

    const { prefix = [], nodeType, groups = [] } = current;

    // 判断是否已经存在
    const index = findIndex(groups, { indexes: get(ins, 'indexes') });

    let mappingType;

    if (nodeType === ARRAY_TYPE) {
      mappingType = ARRAY_MAPPING;
    } else if (nodeType === OBJECT_TYPE) {
      mappingType = OBJECT_MAPPING;
    } else {
      mappingType = LEAF_MAPPING;
    }

    const rules = [
      {
        condition: { type: 'AlwaysTrue' },
        config: {
          mapping: [
            {
              type: 'Path',
              path: sourceNodePath,
            },
          ],
          type: mappingType,
        },
      },
    ];

    if (index !== -1) {
      groups.splice(index, 1, { ...ins, rules });
    } else {
      const p = get(current, 'prefix') || [];
      const g = get(current, 'groupIndex') || 0;
      groups.push({ ...ins, indexes: [...p, g], rules });
    }

    const newNode = {
      ...current,
      groups,
    };

    let newData = setValue(state.data, targetNodePath, newNode);

    newData = fillParentDefaultRule(parent, current, newData);

    message.success(`映射到: ${current.nodeName}`);

    return {
      ...state,
      data: newData,
    };
  }

  if (type === 'update_node_group_attr') {
    const { value, path: nodeAttrPath, parent, current } = payload;

    let newData = setValue(state.data, nodeAttrPath, value);

    newData = fillParentDefaultRule(parent, current, newData);

    message.success(`${current.nodeName}: 配置成功`);

    return {
      ...state,
      data: newData,
    };
  }

  // 新增兄弟节点(操作父节点的children)
  if (type === 'add_sibling_node') {
    const { parent, current, nodePath } = payload;

    const a = nodePath.split('.');
    a.pop();

    const parentNodePath = a.join('.');

    const p = get(parent, 'prefix');
    const g = get(parent, 'groupIndex');

    if (!Array.isArray(p)) {
      throw new Error(`dispatch add_sibling_node error，${parent.nodeName}节点的prefix不是数组`);
    }

    if (!['string', 'number'].includes(typeof g)) {
      throw new Error(
        `dispatch add_sibling_node error，${parent.nodeName}节点的groupIndex不是数字`
      );
    }

    const newNode = {
      ...getDefaultNode(parent),
      groupIndex: 0,
      prefix: [...p, g],
      groups: [],
    };

    if (Array.isArray(get(parent, 'children'))) {
      const index = findIndex(parent.children, { id: current.id });
      // 紧挨着当前节点的下一个位置
      parent.children.splice(index + 1, 0, newNode);
    } else {
      parent.children = [newNode];
    }

    const newData = setValue(state.data, parentNodePath, parent);

    return {
      ...state,
      data: newData,
    };
  }

  // 新增子节点（操作当前节点的children）
  if (type === 'add_child_node') {
    const { current, nodePath } = payload;
    const _current = cloneDeep(current);
    const p = get(_current, 'prefix');
    const g = get(_current, 'groupIndex');

    if (!Array.isArray(p)) {
      throw new Error(`dispatch add_child_node error，${_current.nodeName}节点的prefix不是数组`);
    }

    if (!['string', 'number'].includes(typeof g)) {
      throw new Error(
        `dispatch add_child_node error，${_current.nodeName}节点的groupIndex不是数字`
      );
    }

    const newNode = {
      ...getDefaultNode(current),
      groupIndex: 0,
      prefix: [...p, g],
      groups: [],
    };

    if (hasChildren(_current)) {
      _current.children.push(newNode);
    } else {
      _current.children = [newNode];
    }

    const newData = setValue(state.data, nodePath, _current);

    return {
      ...state,
      data: newData,
    };
  }

  // 删除当前节点
  if (type === 'delete_current_node') {
    const { nodePath } = payload;

    if (nodePath === '') {
      return {
        ...state,
        data: null,
      };
    }

    const newData = setValue(state.data, nodePath, null);

    const c = filterNodes(newData.children, node => node !== null);

    return {
      ...state,
      data: { ...newData, children: c },
    };
  }

  // 切换当前节点的副本
  // 1、修改当前节点的groupIndex等于payload.groupIndex，
  // 2、重置所有子节点的groupIndex为0
  // 2、重置所有子节点的prefix为[...parent.prefix, parent.groupIndex]
  if (type === 'switch_branch') {
    const { groupIndex, current, nodePath } = payload;

    const newNode = cloneDeep(current);
    if (hasChildren(newNode)) {
      newNode.children = mapNodes(
        newNode.children,
        (k, parents) => {
          const parent = last(parents) ? last(parents) : {};

          k.groupIndex = 0;
          if (!Array.isArray(get(parent, 'prefix'))) {
            throw new Error(`节点${k.nodeName}的父元素'prefix'属性不是数组`);
          }

          if (!['string', 'number'].includes(typeof get(parent, 'groupIndex'))) {
            throw new Error(`节点${k.nodeName}的父元素'groupIndex'属性不是number`);
          }

          k.prefix = [...parent.prefix, parent.groupIndex];
          return k;
        },
        [{ ...newNode, groupIndex }]
      );
    }

    return {
      ...state,
      data: setValue(state.data, nodePath, { ...newNode, groupIndex }),
    };
  }

  if (type === 'reset_current_instance') {
    const { current, currentGroup, nodePath } = payload;
    const { groups } = current;
    const newGroups = groups.map(g =>
      g.indexes === currentGroup.indexes ? { ...currentGroup, rules: [] } : g
    );
    return {
      ...state,
      data: setValue(state.data, nodePath, { ...current, groups: newGroups }),
    };
  }

  if (type === 'add_instance') {
    const { current, nodePath } = payload;
    // 1、往current.groups中塞入当前分支下的配置数据 {groupName,indexes,rules}
    // 2、`indexes` 由2个部分组成[...prefix, 父级副本下当前副本添加的顺序]
    // 新增时，currrent.groupIndex 为
    const newNode = cloneDeep(current);

    const { nodeType, prefix = [] } = current;

    if (nodeType === ARRAY_TYPE) {
      const g = getCurrentGroups(newNode);

      const index = g.length;
      newNode.groupIndex = index;

      newNode.groups.push({
        groupName: `副本_${getRangeCode()}`,
        indexes: [...prefix, index],
        rules: [],
      });

      newNode.children = mapNodes(
        newNode.children,
        (k, parents) => {
          const parent = last(parents) ? last(parents) : {};

          k.groupIndex = 0;
          if (!Array.isArray(get(parent, 'prefix'))) {
            throw new Error(`节点${k.nodeName}的父元素'prefix'属性不是数组`);
          }

          if (!['string', 'number'].includes(typeof get(parent, 'groupIndex'))) {
            throw new Error(`节点${k.nodeName}的父元素'groupIndex'属性不是number`);
          }

          k.prefix = [...parent.prefix, parent.groupIndex];
          return k;
        },
        [{ ...newNode, groupIndex: index }]
      );

      return {
        ...state,
        data: setValue(state.data, nodePath, { ...newNode, groupIndex: index }),
      };
    }

    throw new Error('只有数组才能新增副本');
  }

  // 1、修改当前节点的groupIndex等于payload.groupIndex，
  // 2、重置所有子节点的groupIndex为0
  // 2、重置所有子节点的prefix为[...parent.prefix, parent.groupIndex]
  if (type === 'delete_instance') {
    const { group, current, nodePath } = payload;

    const newGroupIndex = last(group.indexes);
    const currentPrefix = group.indexes.slice(0, -1);

    const newNode = cloneDeep(current);

    const deleteIndex = newNode.groups.findIndex(e => isEqual(e.indexes, group.indexes));

    const oldGroupIndex = newNode.groupIndex;
    // 移除的不是当前的，当前节点的groupIndex 不变，否则重置为0

    let gi;
    if (oldGroupIndex === newGroupIndex) {
      gi = 0;
    } else if (oldGroupIndex < newGroupIndex) {
      gi = oldGroupIndex;
    } else {
      gi = oldGroupIndex - 1;
    }

    // 处理当前节点的indexes
    newNode.groups = newNode.groups.map(k => {
      const p = k.indexes.slice(0, -1);
      const lastIndex = last(k.indexes);

      if (lastIndex > newGroupIndex && isEqual(p, currentPrefix)) {
        const l = k.indexes.pop();
        k.indexes = [...k.indexes, l - 1];
      }
      return k;
    });

    // 删掉当前的
    const g = newNode.groups.splice(deleteIndex, 1);

    const prefix = get(g, '[0].indexes');

    if (hasChildren(newNode) && prefix.length > 0) {
      // 当前节点和兄弟节点的公共前缀
      const commonPrefix = prefix.slice(0, -1);
      const joinedCommonPrefix = commonPrefix.join(',');

      newNode.children = mapNodes(
        newNode.children,
        (k, parents) => {
          // 删除不要的副本
          k.groups = (get(k, 'groups') || []).filter(j => {
            return !j.indexes.join(',').startsWith(prefix.join(','));
          });

          const parent = last(parents);

          // prefix: 由2个部分（动态的）组成[...parent.prefix, parent.groupIndex]
          k.prefix = [...parent.prefix, parent.groupIndex];

          k.groups = k.groups.map(item => {
            // 需要动态修改的下标
            const i = newNode.prefix.length;

            const value = item.indexes[i];
            const joinedIndexes = item.indexes.join(',');

            if (value > newGroupIndex && joinedIndexes.startsWith(joinedCommonPrefix)) {
              item.indexes[i] = value - 1;
            }
            return item;
          });

          if (oldGroupIndex === newGroupIndex) {
            k.groupIndex = 0;
          }

          return k;
        },
        [{ ...newNode, groupIndex: gi }]
      );
    }

    return {
      ...state,
      data: setValue(state.data, nodePath, { ...newNode, groupIndex: gi }),
    };
  }

  throw new Error();
}

// 3. 将全局useReducer返回的state和dispatch传递给全局Context.Provider的value中
export const Provider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initState);

  return <Context.Provider value={{ state, dispatch }}>{children}</Context.Provider>;
};
